class AreaSelector {
    // element 是指整个父容器盒子
    constructor({
            // 父容器
            element,
            // 每个格子的选择器
            selectableTarget,
            // 每个格子的 dataset
            datasetKeyForSelection,
            onSelectionChange,
        }) {
        this.element = element;
        this.selectableTarget = selectableTarget;
        this.datasetKeyForSelection = datasetKeyForSelection;
        this.onSelectionChange = onSelectionChange;
        // 选中的格子的 id 集合
        this.selectedIds = [];
        this.#init();
    }
    // private properties
    #area
    #startPosition
    #endPosition
    // private method
    #createSelectArea = () => {
        this.element.style.position = 'relative';
        this.#area = document.createElement('div');
        this.#area.style.position = 'absolute';
        this.#area.style.border = '1px solid rgb(0,119,255)';
        this.#area.style.background = 'rgba(0,119,255,0.2)';
        this.element.appendChild(this.#area);
    }
    #getReltivePositionElement(clientX, clientY) {
        const rect = this.element.getBoundingClientRect();
        const { left, top } = rect;
        const { scrollLeft, scrollTop, scrollWidth, scrollHeight} = this.element;
        let x = clientX - left + scrollLeft;
        let y = clientY - top + scrollTop;
        if(x < 0) {
            x = 0;
        }
        else if(x > scrollWidth) {
            x = scrollWidth;
        }
        if(y < 0) {
            y = 0;
        }
        else if(y > scrollHeight) {
            y = scrollHeight;
        }
        return { x: Math.round(x), y: Math.round(y) }
    }
    #hideArea = () => {
        this.#area.style.display = 'none';
    }
    #showArea = () => {
        this.#area.style.display = 'block';
    }
    #twoRectsHaveIntersection = (rect1, rect2) => {
        const left1 = rect1.left;
        const left2 = rect2.left;
        const right1 = rect1.right;
        const right2 = rect2.right;

        const top1 = rect1.top;
        const top2 = rect2.top;
        const bottom1 = rect1.bottom;
        const bottom2 = rect2.bottom;
        
        const width1 = rect1.width;
        const width2 = rect2.width;
        const height1 = rect1.height;
        const height2 = rect2.height;

        const noIntersection = left2 > right1 || left1 > right2 || top2 > bottom1 || top1 > bottom2 || width1 <= 0 || width2 <= 0 || height1 <= 0 || height2 <= 0;
        return !noIntersection;
    }
    // 计算选中的格子
    #calcSelectedItem = () => {
        const areaRect = this.#area.getBoundingClientRect();
        const items = this.element.querySelectorAll(this.selectableTarget);
        let selectionChange = false;
        [...items].map(item => {
            const itemRect = item.getBoundingClientRect();
            const isSelected = this.#twoRectsHaveIntersection(areaRect, itemRect);
            item.dataset.selected = isSelected;
            const itemId = item.dataset[this.datasetKeyForSelection];
            const index = this.selectedIds.indexOf(itemId);
            if(isSelected) {
                if(index === -1) {
                    this.selectedIds.push(itemId);
                    selectionChange = true;
                }
            }
            else {
                if(index !== -1) {
                    this.selectedIds.splice(index, 1);
                    selectionChange = true;
                }
            }
        })
        if(selectionChange) {
            
            this.onSelectionChange(this.selectedIds);
        }
    }
    #updateArea = () => {
        const left = Math.min(this.#startPosition.x, this.#endPosition.x);
        const top = Math.min(this.#startPosition.y, this.#endPosition.y);
        const width = Math.abs(this.#endPosition.x - this.#startPosition.x);
        const height = Math.abs(this.#endPosition.y - this.#startPosition.y);
        // 减去border的宽度
        this.#area.style.left = left - 8 + 'px';
        this.#area.style.top = top - 8 + 'px';
        this.#area.style.width = width + 'px';
        this.#area.style.height = height + 'px';
    }
    #scrollOnDrg = (clientX, clientY) => {
        const { x, y, width, height } = this.element.getBoundingClientRect();
        let scrollX, scrollY;

        if(clientX < x) {
            scrollX = clientX - x;
        }
        else if(clientX > x + width) {
            scrollX = clientX - (x + width);
        }
        if(clientY < y) {
            scrollY = clientY - y;
        }
        else if(clientY > y + height) {
            scrollY = clientY - (y + height);
        }
        if(scrollX || scrollY) {
            this.element.scrollBy({
                left: scrollX,
                top: scrollY,
                behavior: 'auto'
            })
        }
    }
    #handleMouseDown = (e) => {
        const { clientX, clientY } = e;
        this.#startPosition = this.#getReltivePositionElement(clientX, clientY);
        this.#endPosition = this.#startPosition;
        this.#showArea();
        this.#updateArea();
        this.#scrollOnDrg(clientX, clientY);
        this.#calcSelectedItem();
    }
    #handleMouseMove = (e) => {
        if(e.buttons === 1) {
            const { clientX, clientY } = e;
            this.#endPosition = this.#getReltivePositionElement(clientX, clientY);
            this.#updateArea();
            this.#scrollOnDrg(clientX, clientY);
            this.#calcSelectedItem();
        }
    }
    #init = () => {
        // 创建元素
        this.#createSelectArea();
        // 监听事件
        this.element.addEventListener('mousedown', this.#handleMouseDown)
        window.addEventListener('mousemove', this.#handleMouseMove)
        window.addEventListener('mouseup', e => {
            this.#hideArea();
        })
    }
}
